# encoding: utf8

import Graham_Scan
import numpy as np
import tools


default_mask = 24


def find_start_point(coors):
    sum_values = [x[0] + x[1] for x in coors]
    min_sum = min(sum_values)
    bottom_coor = coors[sum_values.index(min_sum)][:]
    return bottom_coor


def select_same_net_ips(s_ips, d_ips, mask=default_mask):
    result = []
    for s_ip in s_ips:
        s_div = s_ip // (2 ** (32 - mask))
        for j in range(len(d_ips)):
            d_div = d_ips[j] // (2 ** (32 - mask))
            if s_div == d_div:
                result.append(j)

    result = list(set(result))
    result.sort()

    return result


def obtain_geo_center(coors):
    lngs = [x[0] for x in coors]
    lats = [x[1] for x in coors]
    result = [np.mean(lngs), np.mean(lats)]
    return result


def one_cluster(coors, ips):
    start_point = find_start_point(coors)
    cluster_ips = [ips[coors.index(start_point)]]
    cluster_coors = [start_point]

    while True:
        len_c_coors, len_c_ips = len(cluster_coors), len(cluster_ips)
        same_ip_index = select_same_net_ips(cluster_ips, ips)
        for each in same_ip_index:
            if coors[each] not in cluster_coors:
                cluster_coors.append(coors[each])
            if ips[each] not in cluster_ips:
                cluster_ips.append(ips[each])

        area = Graham_Scan.graham_scan(cluster_coors)

        for i in range(len(coors)):
            if tools.isInRegion(coors[i], area):
                if coors[i] not in cluster_coors:
                    cluster_coors.append(coors[i])
                if ips[i] not in cluster_ips:
                    cluster_ips.append(ips[i])

        if len(cluster_coors) == len_c_coors and len(cluster_ips) == len_c_ips:
            break

    if len(area) == 1 or len(area) == 2:
        for each in area:
            cluster_coors.append(each)
    return [cluster_coors, area]


def joint_clustering(coors, ips):
    tmp_coors = coors[:]
    tmp_ips = ips[:]
    cluster_num = 0
    labels = [-1 for i in range(len(coors))]
    geo_center_result = []

    while tmp_coors:
        cluster_coors, area = one_cluster(tmp_coors, tmp_ips)

        geo_center_result.append(obtain_geo_center(cluster_coors))

        for i in range(len(labels)):
            if labels[i] == -1:
                for j in range(len(cluster_coors)):
                    if coors[i] == cluster_coors[j]:
                        labels[i] = cluster_num

                        tmp_ips.pop(tmp_coors.index(cluster_coors[j]))
                        tmp_coors.pop(tmp_coors.index(cluster_coors[j]))

                        cluster_coors.remove(cluster_coors[j])
                        break
        cluster_num += 1

    result = [labels, geo_center_result]
    return result


def net_num(s_ips, mask=default_mask):
    result = []
    for each_s_ip in s_ips:
        s_div = each_s_ip // (2 ** (32 - mask))
        if s_div in result:
            pass
        else:
            result.append(s_div)
    return len(result)


def ave_inter_dis(coors):
    result = 0
    for i in range(len(coors)):
        for j in range(len(coors)):
            result += tools.coors_distance(coors[j][1], coors[j][0], coors[i][1], coors[i][0])
    result = result / (len(coors) ** 2)
    return result


def evaluating_reliability(coors, s_ips, labels):
    s_0 = 50
    alpha, beta = 0.8, 0.5
    re_result = []
    cluster_num = list(set(labels))
    cluster_num.sort()
    for i in range(len(cluster_num)):
        cluster_flag = cluster_num[i]
        cluster_coors = []
        cluster_ips = []
        for j in range(len(labels)):
            if labels[j] == cluster_flag:
                cluster_coors.append(coors[j])
                cluster_ips.append(s_ips[j])

        n = len(cluster_ips)
        m = net_num(cluster_ips)
        s = Graham_Scan.heron_area(cluster_coors)
        d = ave_inter_dis(cluster_coors)

        g_nm = 1 - tools.math.exp(-n / m)
        if s == 0:
            h_s = 0
        else:
            h_s = tools.math.exp(2 * s_0 - s_0 * s_0 / s - s)
        q_d = 1 / (tools.math.log(d + 1, tools.math.e) + 1)

        r = alpha * g_nm + (1 - alpha) * (beta * h_s + (1 - beta) * q_d)
        re_result.append(r)

    return re_result


def my_method(s_data, t_data):
    coors = []
    for i in range(len(s_data)):
        coors.append([s_data[i][1], s_data[i][2]])
    s_ips = [s_data[i][0] for i in range(len(s_data))]

    labels, geo_centers = joint_clustering(coors, s_ips)

    cluster_con = evaluating_reliability(coors, s_ips, labels)

    dis_errors_geo = []
    tar_con = []
    for i in range(len(t_data)):
        adjacent_ip = tools.min_ipnum_dist(s_data, t_data[i][0])
        group_id = labels[s_ips.index(adjacent_ip)]
        tar_con.append(cluster_con[group_id])

        area_center = geo_centers[group_id]                   # geo_centers
        dis_errors_geo.append(tools.coors_distance(area_center[1], area_center[0], t_data[i][2], t_data[i][1]))

    return dis_errors_geo
